// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                              NavigateCurve.C                              //
// ************************************************************************* //

#include <NavigateCurve.h>

#include <VisWindow.h>
#include <VisWindowInteractorProxy.h>

#include <vtkRenderWindowInteractor.h>

// ****************************************************************************
//  Method: NavigateCurve constructor
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
// ****************************************************************************

NavigateCurve::NavigateCurve(VisWindowInteractorProxy &v) : VisitInteractor(v)
{
    ;
}


// ****************************************************************************
//  Method: NavigateCurve::OnTimer
//
//  Purpose:
//    Handles the timer event.  For NavigateCurve, this means the user has
//    pressed a mouse key and that it is time to sample the mouse position
//    to see if the view should be panned or zoomed.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
//  Modifications:
//    Kathleen Bonnell, Wed Jun  8 10:05:08 PDT 2011
//    Use current EventPosition instead of Last.
//
// ****************************************************************************

void
NavigateCurve::OnTimer(void)
{
    vtkRenderWindowInteractor *rwi = Interactor;

    int Pos[2];
    rwi->GetEventPosition(Pos);

    switch (State)
    {
      case VTKIS_PAN:
        PanCamera(Pos[0], Pos[1]);

        rwi->CreateTimer(VTKI_TIMER_UPDATE);
        break;

      case VTKIS_DOLLY:
        ZoomCamera(Pos[0], Pos[1]);

        rwi->CreateTimer(VTKI_TIMER_UPDATE);
        break;

      default:
        break;
    }
}


// ****************************************************************************
//  Method: NavigateCurve::StartLeftButtonAction
//
//  Purpose:
//    Handles the left button being pushed down.  For NavigateCurve, this means
//    panning.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
// ****************************************************************************

void
NavigateCurve::StartLeftButtonAction()
{
    StartPan();
}


// ****************************************************************************
//  Method: NavigateCurve::EndLeftButtonAction
//
//  Purpose:
//    Handles the left button being released.  For NavigateCurve, this means
//    panning.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
//  Modifications:
//    Eric Brugger, Thu Nov 20 15:22:59 PST 2003
//    Added code to call the view callback.
//
// ****************************************************************************

void
NavigateCurve::EndLeftButtonAction()
{
    EndPan();

    IssueViewCallback();
}


// ****************************************************************************
//  Method: NavigateCurve::StartMiddleButtonAction
//
//  Purpose:
//    Handles the middle button being pushed down.  For NavigateCurve, this 
//    means zooming.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
// ****************************************************************************

void
NavigateCurve::StartMiddleButtonAction()
{
    StartDolly();
}


// ****************************************************************************
//  Method: NavigateCurve::EndMiddleButtonAction
//
//  Purpose:
//    Handles the middle button being released.  For NavigateCurve, this means
//    zooming.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
//  Modifications:
//    Eric Brugger, Thu Nov 20 15:22:59 PST 2003
//    Added code to call the view callback.
//
// ****************************************************************************

void
NavigateCurve::EndMiddleButtonAction()
{
    EndDolly();

    IssueViewCallback();
}

// ****************************************************************************
//  Method: NavigateCurve::OnMouseWheelForward()
//
//  Purpose:
//    Handles the mouse wheel turned backward.
//
//  Arguments:
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Mar  2 15:00:32 PST 2012
//
//  Modifications:
//
// ****************************************************************************

void
NavigateCurve::OnMouseWheelForward()
{
    StartZoom();
    ZoomCamera(MotionFactor * 0.2 * this->MouseWheelMotionFactor);
    EndZoom();
    IssueViewCallback(true);
}

// ****************************************************************************
//  Method: NavigateCurve::OnMouseWheelBackward()
//
//  Purpose:
//    Handles the mouse wheel turned forward.  
//
//  Arguments:
//
//  Programmer: Brad Whitlock
//  Creation:   Fri Mar  2 15:00:32 PST 2012
//
//  Modifications:
//
// ****************************************************************************

void
NavigateCurve::OnMouseWheelBackward()
{
    StartZoom();
    ZoomCamera(MotionFactor * -0.2 * this->MouseWheelMotionFactor);
    EndZoom();
    IssueViewCallback(true);
}

// ****************************************************************************
//  Method: NavigateCurve::PanCamera
//
//  Purpose:
//    Handle panning the camera.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
// ****************************************************************************

void
NavigateCurve::PanCamera(const int x, const int y)
{
    vtkRenderWindowInteractor *rwi = Interactor;

    if ((OldX != x) || (OldY != y))
    {
        //
        // Determine the size of the window.
        //
        int       size[2];

        rwi->GetSize(size);

        //
        // Get the current view information.
        //
        VisWindow *vw = proxy;

        double    pan[2];

        avtViewCurve newViewCurve = vw->GetViewCurve();

        pan[0] = (double)(x - OldX) /
                 ((newViewCurve.viewport[1] - newViewCurve.viewport[0]) *
                  (double)(size[0])) *
                 (newViewCurve.domain[1] - newViewCurve.domain[0]);
        pan[1] = (double)(y - OldY) /
                 ((newViewCurve.viewport[3] - newViewCurve.viewport[2]) *
                  (double)(size[1])) *
                 (newViewCurve.range[1] - newViewCurve.range[0]);

        newViewCurve.domain[0] -= pan[0];
        newViewCurve.domain[1] -= pan[0];
        newViewCurve.range[0]  -= pan[1];
        newViewCurve.range[1]  -= pan[1];

        vw->SetViewCurve(newViewCurve);

        OldX = x;
        OldY = y;
        rwi->Render();
    }
}

// ****************************************************************************
//  Method: NavigateCurve::ZoomCamera
//
//  Purpose:
//    Handle zooming the camera.
//
//  Programmer: Eric Brugger
//  Creation:   October 15, 2003
//
//  Modifications:
//    Brad Whitlock, Fri Mar  2 14:57:11 PST 2012
//    I moved the guts into ZoomCamera.
//
// ****************************************************************************

void
NavigateCurve::ZoomCamera(const int x, const int y)
{
    if (OldY != y)
    {
        //
        // Calculate the zoom factor.
        //
        double dyf = MotionFactor * (double)(y - OldY) /
                         (double)(Center[1]);

        ZoomCamera(dyf);

        OldX = x;
        OldY = y;
    }
}

// ****************************************************************************
// Method: NavigateCurve::ZoomCamera
//
// Purpose: 
//   Zoom the camera based on a zoom factor.
//
// Arguments:
//   f : The zoom factor.
//
// Programmer: Eric Brugger
// Creation:   Fri Mar  2 14:57:21 PST 2012
//
// Modifications:
//   Brad Whitlock, Fri Mar  2 15:00:02 PST 2012
//   I made this routine from the other ZoomCamera routine.
//
// ****************************************************************************

void
NavigateCurve::ZoomCamera(double f)
{
    double zoomFactor = pow((double)1.1, f);

    //
    // Calculate the new parallel scale.
    //
    VisWindow *vw = proxy;

    avtViewCurve newViewCurve = vw->GetViewCurve();
    double dX = ((1. / zoomFactor) - 1.) *
                ((newViewCurve.domain[1] - newViewCurve.domain[0]) / 2.);
    double dY = ((1. / zoomFactor) - 1.) *
                ((newViewCurve.range[1] - newViewCurve.range[0]) / 2.);

    newViewCurve.domain[0] -= dX;
    newViewCurve.domain[1] += dX;
    newViewCurve.range[0]  -= dY;
    newViewCurve.range[1]  += dY;

    vw->SetViewCurve(newViewCurve);
}
